package belf.migrate.engine.util;

import belf.migrate.api.util.ExceptionUtils;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

@Slf4j
public class JDBCDriverUtil {
    /**
     * 查找指定的jdbc jar文件中有哪些实现了(@link java.sql.Driver}的类。
     * 该方法不会注册Driver，只是返回所有Driver的实现类，让用户选择加载哪个。<br/>
     * 注意：本方法有缺陷，需要将所有类都加载了才能判断Driver。最好不用本方法。
     * @param jdbcJarPath
     * @return 所有Driver的实现类的名称
     * @throws Exception
     */
    public List<String> findJDBCDrivers(String jdbcJarPath) throws Exception {
        URL jdbcUrl = new File(jdbcJarPath).toURI().toURL();
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{jdbcUrl}, this.getClass().getClassLoader());
        List<String> driverNames = new ArrayList<>();

        File jarFile = new File(urlClassLoader.getURLs()[0].toURI());
        try (JarFile jar = new JarFile(jarFile)) {
            Enumeration<JarEntry> entries = jar.entries();
            while (entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                String entryName = entry.getName();

                if (entryName.endsWith(".class")) {
                    String className = entryName.replace("/", ".").replace(".class", "");
                    try {
                        // 这一行很危险，很可能重复加载Driver
                        Class<?> clazz = Class.forName(className, true, urlClassLoader);
                        if (Driver.class.isAssignableFrom(clazz)) {
                            String driverClassName = className;
                            log.info("Found JDBC Driver: {}", driverClassName);
                            driverNames.add(driverClassName);
                        }
                    } catch (Throwable ignored) {

                    }
                }
            }
        }
        return driverNames;
    }

    /**
     * 从指定的jar文件加载JDBC驱动
     * @param jdbcJarPath
     * @param driverClass
     * @throws Exception
     */
    public void registerDriver(String jdbcJarPath, String driverClass) {
        try {
            URL jdbcUrl = new File(jdbcJarPath).toURI().toURL();
            URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{jdbcUrl}, this.getClass().getClassLoader());
            Class<?> clazz = Class.forName(driverClass, true, urlClassLoader);
            Driver driver = (Driver) clazz.getDeclaredConstructor().newInstance();
            DriverManager.registerDriver(driver);
        } catch (Exception e) {
            log.error("Error while loading JDBC Driver {} from {} error:\n{}",
                    driverClass,
                    jdbcJarPath,
                    ExceptionUtils.toString(e));
        }
    }
}
